package views;

import java.util.HashMap;
import java.util.Map;

import model.Problem;
import model.Solution;
import model.SolverEnvironment;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import solver.CollaboratingSolvers;
import solver.ProblemDecompositionMathSolver;
import util.Chronograph;
import util.Importer;
import util.SolutionImporter;
import etm.core.configuration.BasicEtmConfigurator;
import etm.core.configuration.EtmManager;
import etm.core.monitor.EtmMonitor;
import etm.core.renderer.SimpleTextRenderer;

public class MachineReassignmentMay2012 {
	private static Logger log = LoggerFactory
			.getLogger(MachineReassignmentMay2012.class);
	private EtmMonitor monitor;

	static {
		String os_name = System.getProperty("os.name").toLowerCase();
		String os_arch = System.getProperty("os.arch").toLowerCase();
		if ((os_name.indexOf("win") >= 0) && (os_arch.indexOf("64") > 0))
			System.loadLibrary("libwin64//jnilinearsolver");
		else
			System.loadLibrary("jnilinearsolver");
	}

	// ###################### RUN PARAMETERS ######################
	// final static String problemid = "b_7";
	final static String problemid = "a1_2";
	final static int time_limit = 300;
	final static long randomSeed = 1821L;
	final static boolean use_grb = false;
	final static boolean saveLP = false;
	final static boolean visualize = true;
	final static int number_of_neighborhoods = -1; // if number_of_neighborhoods
													// is -1 then problem.N/2
	final static int number_of_eligible_machines = 30;
	final static int number_of_processes = 50;
	final static int limitXVars = 500; // if limitXVars is -1 then no limit to
										// variables is applied
	final static boolean include_dependency_constraints = true;
	final static boolean include_collaborating_solvers = false;
	final static int time_limit_single_problem = 3;

	// ############################################################

	public static void main(String[] args) {
		Chronograph chronos = new Chronograph();
		chronos.reset();
		String dataset = String.format("datasets//model_%s.txt", problemid);
		String initial_solution_s = String.format(
				"solutions//assignment_%s.txt", problemid);
		MachineReassignmentMay2012 mare2 = new MachineReassignmentMay2012();
		mare2.setUseGRB(use_grb);
		mare2.launch(time_limit, dataset, initial_solution_s, "output",
				randomSeed);
		SolverEnvironment env = mare2.getSolverEnvironment();
		env.getFullValidator().computeCost();
		long overall_cost_i = env.getFullValidator().getNewSolutionCost();
		long overall_cost_r = env.getFullValidator().getInitialSolutionCost();
		log.debug(String.format(
				"%s INITIAL_SOLUTION_COST=%,d NEW_COST=%,d (R=%d)\n", dataset,
				overall_cost_r, overall_cost_i, mare2.getRounds()));
		env.getFullValidator().printCostAnalysis();
		chronos.lap();
		log.debug("Time elapsed: " + chronos.getElapsedTimeInSeconds());
	}

	Map<String, Long> best_solutions;

	public void initializations() {
		best_solutions = new HashMap<String, Long>();
		best_solutions.put("a1_1", 44306501L);
		best_solutions.put("a1_2", 777532896L);
		best_solutions.put("a1_3", 583005717L);
		best_solutions.put("a1_4", 252728589L);
		best_solutions.put("a1_5", 727578309L);
		best_solutions.put("a2_1", 198L);
		best_solutions.put("a2_2", 816523983L);
		best_solutions.put("a2_3", 1306868761L);
		best_solutions.put("a2_4", 1681353943L);
		best_solutions.put("a2_5", 336170182L);
		best_solutions.put("b_1", 3290754940L);
		best_solutions.put("b_2", 1015153860L);
		best_solutions.put("b_3", 156631070L);
		best_solutions.put("b_4", 4677767120L);
		best_solutions.put("b_5", 922858550L);
		best_solutions.put("b_6", 9525841820L);
		best_solutions.put("b_7", 14833996360L);
		best_solutions.put("b_8", 1214153440L);
		best_solutions.put("b_9", 15885369400L);
		best_solutions.put("b_10", 18048006980L);
	}

	public void launch(int time_limit, String instance_filename,
			String original_solution_filename, String new_solution_filename,
			long seed) {
		setup();
		initializations();
		Importer importer = new Importer(instance_filename);
		importer.readData();
		Problem problem = importer.getProblem();
		SolutionImporter solutionImporter = new SolutionImporter(problem);
		solutionImporter.setSolutionFileName(original_solution_filename);
		Solution initial_solution = solutionImporter.readData();
		env = new SolverEnvironment(problem, initial_solution);

		if (include_collaborating_solvers) {
			CollaboratingSolvers solver = new CollaboratingSolvers(problem,
					initial_solution);
			int usedTime = solver.solveWithTimeLimit(time_limit / 3);
			time_limit -= usedTime;
			env.setCurrentSolution(solver.getNewSolution());
			// problem.computeTransientResourcesRemainingCapacityPerMachine(env);
		}

		int non;
		if (number_of_neighborhoods == -1)
			non = problem.N / 2;
		else
			non = number_of_neighborhoods;

		ipsolver = new ProblemDecompositionMathSolver(env, useGRB, visualize);
		ipsolver.setIncludeDependencyConstraints(include_dependency_constraints);
		ipsolver.set_nop_noem_non(number_of_processes,
				number_of_eligible_machines, non);
		ipsolver.setSaveLP(saveLP);
		ipsolver.setLimitXVars(limitXVars);
		ipsolver.setTime_limit_single_problem(time_limit_single_problem);
		ipsolver.solveWithTimeLimit(time_limit);
		ipsolver.revertToBestSolution();
		String s = instance_filename.replace(".txt", "");
		s = s.replace("datasets//model_", "").trim();
		log.info(String.format("Cost is %.4f", getScore(s)));
		if (visualize)
			ipsolver.closeWindow();
		monitor.render(new SimpleTextRenderer());
		ipsolver.saveSolution(s, "CS_MATH_SOLVER");
		tearDown();
	}

	SolverEnvironment env;
	boolean useGRB;
	ProblemDecompositionMathSolver ipsolver;

	public boolean isUseGRB() {
		return useGRB;
	}

	public void setUseGRB(boolean useGRB) {
		this.useGRB = useGRB;
	}

	public SolverEnvironment getSolverEnvironment() {
		return env;
	}

	public int getRounds() {
		return ipsolver.getRounds();
	}

	private void setup() {
		BasicEtmConfigurator.configure();
		monitor = EtmManager.getEtmMonitor();
		monitor.start();
	}

	private void tearDown() {
		monitor.stop();
	}

	public double getScore(String pro_id) {
		env.getFullValidator().computeCost();
		long overall_cost_i = env.getFullValidator().getNewSolutionCost();
		long overall_cost_r = env.getFullValidator().getInitialSolutionCost();
		long overall_cost_b = best_solutions.get(pro_id);
		log.info(String.format("Initial cost=%,d new_cost=%,d best_cost=%,d",
				overall_cost_r, overall_cost_i, overall_cost_b));
		double score = (overall_cost_i - overall_cost_b)
				/ (double) overall_cost_r;
		return score * 100;
	}
}
